using System;

using TrueCode.Core.Aspects;


namespace TrueCode.DependencyManager.BuildActions.TypeMapping
{
  public class MapType : IMapType
  {
    private readonly Type _type;
    private readonly bool _checkAssignability;

    public MapType( [NotNull] Type mapToType ) : this( mapToType, true )
    {
    }

    public MapType( [NotNull] Type mapToType, bool checkAssignability )
    {
      if( mapToType == null ) throw new ArgumentNullException( "mapToType" );

      _type = mapToType;
      _checkAssignability = checkAssignability;
    }

    public virtual void Execute( IBuildSession context )
    {
      var type = context.SessionAim.Type;

      #region Assert that aim type is assignable from _type
      if( _checkAssignability && !type.IsAssignableFrom( _type ) )
      {
        var exc = new InvalidOperationException( "Type mapping is not available." );
        exc.Data.Add( "SourceType", type );
        exc.Data.Add( "TargetType", _type );
        throw exc;
      }
      #endregion

      context.SessionAim = new SessionAim( _type, context.SessionAim.Setting );
      context.ExecuteNextBuildAction();
    }

    public static MapType To< TTo >()
    {
      return To< TTo >( true );
    }

    public static MapType To< TTo >( bool checkAssignability )
    {
      return new MapType( typeof( TTo ), checkAssignability );
    }
  }

  public class MapType< TTo > : MapType
  {
    public MapType() : base( typeof( TTo ) )
    {
    }

    public MapType( bool checkAssignability ) : base( typeof( TTo ), checkAssignability )
    {
    }
  }
}